/**
 * Created by Forcs on 16/2/4.
 */
(function (ctx) {
  var comp = ctx.wcomponents || {};
  var _$ = jQuery || ctx.jQuery || undefined;

  var extend = function(options, def) {
    for (var key in def) {
      if (!options.hasOwnProperty(key)) {
        options[key] = def[key];
      }
    }
    return options;
  };

  // 组件基本属性,每个组件都必须继承自该属性
  var baseProps = {
    viewId: {
      type: Number,
      require: true
    }
  };

  var iconFontProps = extend({
    font : String
    , fontSize : {
      type : String
      , default : ''
    }
    , fontProvider : {
      type : String
      , default : 'fa'
    }
    , fixedWidth : {
      type : Boolean
      , default : false
    }
    , isSpin : {
      type : Boolean
      , default : false
    }
    , feedback : {
      type : Boolean
      , default : false
    }
  }, baseProps);
  comp.IconFont = Vue.extend({
    template : "<span class='' :class='[fontProvider, iconName, isSpin ? \"fa-spin\" : \"\", iconSize, feedback ? \"form-control-feedback\" : \"\"]'></span>",
    props : iconFontProps,
    computed : {
      iconName : function() {
        return this.font ? this.fontProvider + '-' + this.font : '';
      }
      , iconSize : function() {
        if (!this.fontSize) {
          return ''
        } else {
          return 'fa-' + this.fontSize;
        }
      }
    }
  });
  Vue.component("icon", comp.IconFont);


  var defFieldProps = extend({
    label : String
    , type : {
      type : String
      , default : 'text'
    }
    , placeholder : {
      type : String
      , default : ''
    }
    , defaultValue : {
      type : String
      , default : ''
    }
    , fieldId : {
      type : String
      , require : true
    }
    , hasFeedback : {
      type : Boolean
      , default : false
    }
  }, baseProps);
  var baseFieldMethods = {
    feedbackOk : function() {
      var self = this;
      self.feedbackType = 'has-success';
      self.$refs.feedbackComponent.font = 'ok';
    }
    , feedbackWarning : function() {
      var self = this;
      self.feedbackType = 'has-warning';
      self.$refs.feedbackComponent.font = 'warning-sign';
    }
    , feedbackError : function() {
      var self = this;
      self.feedbackType = 'has-error';
      self.$refs.feedbackComponent.font = 'remove';
    }
  };
  comp.InputField = Vue.extend({
    template :  '<div class="form-group" :class="[hasFeedback ? \'has-feedback\' : \'\', feedbackType]">' +
                  '<label for="${ fieldId }" class="control-label">${ label }</label>' +
                  '<input type="${ type }" v-model="inputText" @input="onInput" class="form-control" name="${ fieldId}" id="${ fieldId }" value="${ defaultValue }" placeholder="${ placeholder }"/>' +
                  '<component :is="iconComponent" font-provider="glyphicon" feedback v-if="hasFeedback" v-ref:feedback-component></component>' +
                '</div>'
    , props : defFieldProps
    , components : {
      icon : comp.IconFont
    }
    , data : function() {
      return {
        inputText : ''
        , iconComponent : 'icon'
        , feedbackType : ''
      }
    }
    , methods : extend({
      onInput : function(event) {
        var self = this;
        if (self.inputText.trim()) {
          self.$dispatch('input', this.viewId, event, self.inputText);
        }
      }
    }, baseFieldMethods)
  });
  Vue.component("input-field", comp.InputField);


  var fileFieldProps = extend({
    type : {
      type : String
      , default : 'file'
    }
    , uploadUrl : {
      type : String
      , require : true
    }
    , dataType : {
      type : String
      , default : 'json'
    }
    , allowedType : {
      type : String
      , default : '*/*'
    }
  }, defFieldProps);
  comp.FileField = Vue.extend({
    template : '<div class="form-group" :class="[hasFeedback ? \'has-feedback\' : \'\', feedbackType]">' +
                  '<label for="${ fieldId }" class="control-label">${ label }</label>' +
                  '<input v-el:input-component type="file" class="form-control" name="${ fieldId}" id="${ fieldId }" value="${ defaultValue }" placeholder="${ placeholder }"/>' +
                  '<component :is="iconComponent" font-provider="glyphicon" feedback v-if="hasFeedback" v-ref:feedback-component></component>' +
                '</div>'
    , props : fileFieldProps
    , data : function() {
      return {
        iconComponent : 'icon'
      }
    }
    , components : {
      icon : comp.IconFont
    }
    , methods : baseFieldMethods
    , ready : function() {
      var self = this;
      var jQueryObj = _$(self.$el);
      if (!jQueryObj.dmUploader) {
        return;
      }
      jQueryObj.dmUploader({
        url : self.uploadUrl
        , dataType : self.dataType
        , allowedType : self.allowedType
        , onInit : function() {
          self.$dispatch('init', self, self.$el);
        }
        , onBeforeUpload : function(id) {
          self.$dispatch('before-upload', self, id, self.$el);
        }
        , onComplete : function() {
          self.$dispatch('complete', self, self.$el);
        }
        , onUploadSuccess : function(id, data) {
          self.$dispatch('upload-success', self, id, data, self.$el);
        }
        , onUploadError : function(id, message) {
          self.$dispatch('upload-error', self, id, message, self.$el);
        }
        , onFileTypeError : function(file) {
          self.$dispatch('filetype-error', self, file, self.$el);
        }
      });
    }
  });
  Vue.component("file-field", comp.FileField);


  var gridProps = extend({
    headers : Array
    , items : Array
    , headerClickable : {
      type : Function
      , default : function(column) {
        return false;
      }
    }
    , isComponentCell : {
      type : Function
      , default : function(column) {
        return false;
      }
    }
    , cellType : {
      type : Function
      , require : true
    }
    , cellData : {
      type : Function
      , require : true
    }
  }, baseProps);
  comp.GridView = Vue.extend({
    template :
      "<table class='table table-hover'>" +
        "<thead>" +
          "<tr>" +
            "<th v-for='(index, header) in headers' :class=\"{ 'clickable': headerClickable(index) }\"  @click='headerClickable(index) ? onClickColumnHeader($event, index, header) : \"\"'>${ header }</th>" +
          "</tr>" +
        "</thead>" +
        "<tbody>" +
          "<tr v-for='(row, item) in items'>" +
            "<td v-for='(column, cell) in item'>" +
              "<component :is='cellType(row, column, cell)' :data='cellData(row, column, cell)' v-if='isComponentCell(column)'></component>" +
              "<span v-else>${ cellData(row, column, cell) }</span>" +
            "</td>" +
          "</tr>" +
        "</tbody>" +
      "</table>",
    props : gridProps,
    methods : {
      onClickColumnHeader : function(event, index, header) {
        var self = this;
        self.$dispatch('click-column-header', self, self.viewId, index, header, event.target);
      }
    }
  });
  Vue.component("grid-view", comp.GridView);


  var listProps = extend({
    items : Array
    , itemType : {
      type : Function
      , default : function(index) {
        return undefined;
      }
    }
    , itemData : {
      type : Function
      , require : true
    }
    , itemClickable : {
      type : Boolean
      , default : true
    }
    , maxHeight : Number
  }, baseProps);
  comp.ListView = Vue.extend({
    template :
        "<div class='w-list-view'><div class='list-group w-list-group-fixed'>" +
          "<a class='list-group-item w-list-group-item-fixed' v-for='(index, item) in items' @click='itemClickable ? onClickItem($event, index, item) : \"\"'>" +
            "<component :is='itemType(index)' :data='itemData(index, item)' v-if='itemType(index)'></component>" +
            "<span v-else>${ itemData(index, item) }</span>" +
          "</a>" +
        "</div></div>",
    props : listProps,
    methods : {
      onClickItem : function(event, index, item) {
        var self = this;
        self.$dispatch("click-item", self, self.$children[index], index, item, event.target);
      }
    },
    ready : function() {
      var self = this;
      var el = _$(self.$el);
      el.css({
        overflow: "hidden",
        position : 'relative'
      });
      el.perfectScrollbar();
      var height = el.height();
      if (height > self.maxHeight) {
        el.height(self.maxHeight);
      }
    },
    watch : {
      items : function(newValue, oldValue) {
        var self = this;
        var el = _$(self.$el);
        var height = el.height();
        if (height > self.maxHeight) {
          el.height(self.maxHeight);
        }
      }
    }
  });
  Vue.component("list-view", comp.ListView);


  var SYNC_DEFAULT_STATE_TAG = 0;
  var SYNC_PREVIEW_STATE_TAG = 1;
  var SYNC_RELEASE_STATE_TAG = 2;

  var syncBtnProps = extend({
    previewText : {
      type : String
      , default : '预览'
    }
    , previewAction : {
      type : String
      , require : true
    }
    , releaseText : {
      type : String
      , default : '发布'
    }
    , releaseAction : {
      type : String
      , require : true
    }
    , actionType : {
      type : String
      , default : 'POST'
    }
    , actionDataType : {
      type : String
      , default : 'json'
    }
    , syncState : {
      type : Number
      , default : SYNC_DEFAULT_STATE_TAG
    }
    , buttonText : {
      type : String
      , require : true
    }
    , buttonSize : {
      type : String
      , default : ''
    }
    , syncData : {
      type : Function
      , require : true
    }
    , buttonBlock : {
      type : Boolean
      , default : false
    }
  }, baseProps);

  comp.SyncButton = Vue.extend({
    template :
        "<button type='button' class='btn' :class='[btnStyle, btnSize, syncing ? \"disabled\" : \"\", buttonBlock ? \"btn-block\" : \"\"]' @click='onSyncButtonClick'>" +
            "<span class='fa fa-circle-o-notch fa-spin' :class='[spinSize]' v-if='syncing'></span>" +
            "<span v-else>${ btnText }</span>" +
        "</button>"
    , props : syncBtnProps
    , data : function() {
      return {
        syncing : false
        , btnStyle : 'btn-default'
      }
    }
    , computed : {
      btnSize : function() {
        var self = this;
        return self.buttonSize ? 'btn-' + this.buttonSize : '';
      }
      , btnText : function() {
        var self = this;
        var txt = self.buttonText;
        if (self.syncState === SYNC_DEFAULT_STATE_TAG) {
          txt = self.previewText;
        } else if (self.syncState === SYNC_PREVIEW_STATE_TAG) {
          txt = self.releaseText;
        }
        return txt;
      }
      , btnStyle : function() {
        var self = this;
        if (self.syncState === SYNC_DEFAULT_STATE_TAG) {
          return 'btn-info';
        } else if (self.syncState === SYNC_PREVIEW_STATE_TAG) {
          return 'btn-success';
        } else {
          return ['btn-success', 'disabled'];
        }
      }
      , spinSize : function() {
        var self = this;
        var fs = 'lg';
        switch (self.buttonSize) {
          case 'xs':
            fs = '';
            break;
          case 'lg':
            fs = '2x';
            break;
          default:
            fs = ''
        }
        return fs;
      }
    }
    , watch : {
      syncState : function(newVal, oldVal) {
        var self = this;
        if (!self.syncing) {
          self.syncing = false;
        }
      }
    }
    , methods : {
      onSyncButtonClick : function(event) {
        var self = this;
        var handleSyncClick = function(state, action, syncData) {
          self.syncing = true;
          var data = syncData();
          _$.ajax(action, {
            type : self.actionType
            , data : data
            , dataType : self.actionDataType
          }).done(function(data) {
            self.syncing = false;
            self.$dispatch('sync-ok', self, state, data, self.$el);
          }).fail(function() {
            self.syncing = false;
            self.$dispatch('sync-error', self, state, self.$el);
          });
        };

        if (self.syncState === SYNC_DEFAULT_STATE_TAG) {
          handleSyncClick(self.syncState, self.previewAction, self.syncData);
        } else if (self.syncState === SYNC_PREVIEW_STATE_TAG) {
          handleSyncClick(self.syncState, self.releaseAction, self.syncData);
        }
      }
      , nextState : function() {
        var self = this;
        var state = self.syncState;
        state = Math.min(++state, SYNC_RELEASE_STATE_TAG);
        self.syncState = state;
      }
      , currentState : function() {
        return this.syncState;
      }
    }
  });
  Vue.component("sync-button", comp.SyncButton);

  var panelProps = extend({
    withoutHeading : {
      type : Boolean
      , default : false
    }
    , withoutBody : {
      type : Boolean
      , default : false
    }
    , withoutFooter : {
      type : Boolean
      , default : true
    }
  }, baseProps);
  comp.Panel = Vue.extend({
    template :
        "<div class='panel panel-default' :class='[panelType]'>" +
          "<div class='panel-heading' v-el:heading-element v-if='!withoutHeading'>" +
            "<slot name='heading'></slot>" +
          "</div>" +
          "<div class='panel-body' v-if='!withoutBody'>" +
            "<slot name='body'></slot>" +
          "</div>" +
          "<slot name='x'></slot>" +
          "<div class='panel-footer' v-if='!withoutFooter'>" +
            "<slot name='footer'></slot>" +
          "</div>" +
        "</div>"
    , props : panelProps
    , ready : function() {
      var self = this;
      var headingElem = self.$els.headingElement || undefined;
      if (headingElem) {
        _$(headingElem).children().each(function(index, child) {
          var ch = _$(child);
          if (/^h|h1|h2|h3|h4|h5|h6$/.test(ch[0].tagName.toLowerCase())) {
            ch.addClass('panel-title');
          }
        });
      }
    }
  });
  Vue.component("panel", comp.Panel);

  var videoProps = extend({
    resolution : {
      type : String
      , default : "16:9"
    }
    , sources : Array
    , currentSrc : {
      type : String
      , default : ''
    }
    , currentIndex : {
      type : Number
      , default : 0
    }
    , cover : String
  }, baseProps);
  comp.VideoView = Vue.extend({
    template :
        "<video autoplay :poster='cover' width='100%'" +
              "@abort='onVideoEvent(\"abort\", $event)' " +
              "@canplay='onVideoEvent(\"can-play\", $event)' " +
              "@canplaythrough='onVideoEvent(\"can-play-through\", $event)' " +
              "@durationchangenew='onVideoEvent(\"duration-change-new\", $event)' " +
              "@emptied='onVideoEvent(\"emptied\", $event)' " +
              "@ended='onVideoEvent(\"ended\", $event)' " +
              "@error='onVideoEvent(\"error\", $event)' " +
              "@loadeddata='onVideoEvent(\"loaded-data\", $event)' " +
              "@loadedmetadata='onVideoEvent(\"loaded-metadata\", $event)' " +
              "@loadstart='onVideoEvent(\"load-start\", $event)' " +
              "@pause='onVideoEvent(\"pause\", $event)' " +
              "@play='onVideoEvent(\"play\", $event)' " +
              "@playing='onVideoEvent(\"playing\", $event)' " +
              "@progress='onVideoEvent(\"progress\", $event)' " +
              "@ratechangeNew='onVideoEvent(\"rate-change-new\", $event)' " +
              "@readystatechange='onVideoEvent(\"ready-state-change\", $event)' " +
              "@seeking='onVideoEvent(\"seeking\", $event)' " +
              "@seeked='onVideoEvent(\"seeked\", $event)' " +
              "@stalled='onVideoEvent(\"stalled\", $event)' " +
              "@suspend='onVideoEvent(\"suspend\", $event)' " +
              "@timeupdate='onVideoEvent(\"time-update\", $event)' " +
              "@volumechange='onVideoEvent(\"volume-change\", $event)' " +
              "@waiting='onVideoEvent(\"waiting\", $event)' :src='currentSrc'>" +
        "</video>"
    , props : videoProps
    , watch : {
      sources : function(newVal, oldVal) {
        var self = this;
        var el = self.$el;
        if (newVal && newVal.length > 0) {
          if (el.paused) {
            self.currentSrc = newVal[self.currentIndex];
          }
        } else {
          self.currentSrc = '';
        }

        if (el.paused && self.currentSrc) {
          el.load();
        }
      }
    }
    , methods : {
      onVideoEvent : function(eventName, event) {
        var self = this;
        if (eventName === 'ended') {
          if (self.sources && self.sources.length) {
            var player = event.target;
            if (++self.currentIndex >= self.sources.length) {
              self.currentIndex = 0;
            }
            self.currentSrc = self.sources[self.currentIndex];
            player.load();
          }
        }
        console.log("@@@ video event " + eventName);
        self.$dispatch("video-" + eventName, self, event, self.$el);
      }
    }
    , ready : function() {
      var self = this;
      var el = _$(self.$el);
      var w = el.width();
      if (self.resolution.indexOf(":") > 0) {
        var reso = self.resolution.split(":");
        var h = parseInt((parseFloat(w) / parseInt(reso[0])) * parseInt(reso[1]));
        if (h !== el.height()) {
          el.height(h);
        }
      }
    }
  });
  Vue.component("video-view", comp.VideoView);

  ctx.wcomponents = comp;

}) (window);